package com.zh.media.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zh.common.exception.BusinessException;
import com.zh.common.properties.JwtProperties;
import com.zh.common.utils.RedisUtil;
import com.zh.media.domain.dto.AuditVideoDto;
import com.zh.media.domain.dto.PageVideoDto;
import com.zh.media.domain.dto.UpdateVideoDto;
import com.zh.media.domain.dto.UpdateWorkInfoDto;
import com.zh.media.service.WorkInfoService;
import com.zh.web.base.PageApiResult;
import com.zh.web.domain.media.entity.WorkInfo;
import com.zh.web.domain.media.vo.VideoVo;
import com.zh.web.domain.auth.dto.UserDto;
import com.zh.web.domain.auth.vo.UserVo;
import com.zh.web.domain.media.entity.Video;
import com.zh.media.mapper.VideoMapper;
import com.zh.media.service.VideoService;
import com.zh.web.domain.system.dto.DeleteFileDto;
import com.zh.web.enums.State;
import com.zh.web.openApi.AuthClient;
import com.zh.web.openApi.FileClient;
import com.zh.web.openApi.UserClient;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;

import static com.zh.media.Thread.FixedThreadPool.fixedThreadPool;
import static com.zh.media.config.RedisKeyConfig.*;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author zh
 * @since 2024-10-02
 */
@Service
public class VideoServiceImpl extends ServiceImpl<VideoMapper, Video> implements VideoService {
    Set<String> allowedSuffixes = new HashSet<>(Arrays.asList(
            "mp4", "avi", "mkv", "mov", "flv", "wmv", "mpg", "mpeg", "m4v", "webm"
    ));

    private final VideoMapper videoMapper;

    private final WorkInfoService workInfoService;
    private final FileClient fileClient;
    private final UserClient userClient;
    private final AuthClient authClient;

    private final RedisUtil redisUtil;
    private final JwtProperties jwtProperties;
    private final AmqpTemplate amqpTemplate;

    public VideoServiceImpl(VideoMapper videoMapper, WorkInfoService workInfoService, FileClient fileClient, UserClient userClient, AuthClient authClient, RedisUtil redisUtil, JwtProperties jwtProperties, AmqpTemplate amqpTemplate) {
        this.videoMapper = videoMapper;
        this.workInfoService = workInfoService;
        this.fileClient = fileClient;
        this.userClient = userClient;
        this.authClient = authClient;
        this.redisUtil = redisUtil;
        this.jwtProperties = jwtProperties;
        this.amqpTemplate = amqpTemplate;
    }

    @Override
    public VideoVo getDetail(Long id, HttpServletRequest request) {
        Video video = videoMapper.selectById(id);
        if (null == video) {
            return null;
        }
        Long userId;
        String token = request.getHeader(jwtProperties.getTokenName());
        Map<String, Object> userMap = authClient.getSessionByToken(token).getData();
        if (null != userMap) {
            userId = Long.parseLong(userMap.get("id").toString());
        } else {
            userId = null;
        }
        VideoVo videoVo = BeanUtil.copyProperties(video, VideoVo.class);
        CompletableFuture<Void> praiseTask = CompletableFuture.runAsync(() -> {
            Integer praiseCount = workInfoService.countPraise(id);
            videoVo.setPraiseCount(praiseCount);
        }, fixedThreadPool);
        CompletableFuture<Void> collectTask = CompletableFuture.runAsync(() -> {
            Integer collectCount = workInfoService.countCollect(id);
            videoVo.setCollectCount(collectCount);
        }, fixedThreadPool);
        CompletableFuture<Void> viewTask = CompletableFuture.runAsync(() -> {
            Integer viewCount = workInfoService.countView(id);
            videoVo.setViewCount(viewCount);
        }, fixedThreadPool);
        CompletableFuture.runAsync(() -> {
            Integer shareCount = workInfoService.countShare(id);
            videoVo.setShareCount(shareCount);
        }, fixedThreadPool);
        CompletableFuture<Void> commentTask = CompletableFuture.runAsync(() -> {
            //回复 todo
//            Integer commentCount = workInfoService.countComment(id);
        }, fixedThreadPool);
        CompletableFuture<Void> meTask = CompletableFuture.runAsync(() -> {
            if (userId != null) {
                //我是否已点赞、收藏
                WorkInfo workInfo = workInfoService.selectByIdAndUserId(id, userId);
                if (null == workInfo) {
                    return;
                }
                videoVo.setIsCollected(State.CONFIRM.equals(workInfo.getCollect()));
                videoVo.setIsPraised(State.CONFIRM.equals(workInfo.getPraise()));
            }
        }, fixedThreadPool);
        CompletableFuture.allOf(praiseTask, collectTask, viewTask, commentTask, meTask).join();
        return videoVo;
    }

    @Override
    public PageApiResult<VideoVo> pageVideo(PageVideoDto pageVideoDto) {
        pageVideoDto.setPageNum(pageVideoDto.getPageNum() < 1L ? 1L : pageVideoDto.getPageNum());
        pageVideoDto.setPageSize(pageVideoDto.getPageSize() < 1L ? 10L : pageVideoDto.getPageSize());
        IPage<VideoVo> page = new Page<>(pageVideoDto.getPageNum(), pageVideoDto.getPageSize());
        List<VideoVo> videoVoList = videoMapper.page(page, pageVideoDto);
        page.setRecords(videoVoList);
        return PageApiResult.<VideoVo>builder()
                .pageNum(page.getCurrent())
                .pageSize(page.getSize())
                .list(page.getRecords())
                .build();
    }

    @Override
    public List<VideoVo> listVideoVoByIds(List<Long> ids) {
        return videoMapper.listVideo(ids);
    }

    @Override
    public List<VideoVo> listVideoAfterTime(LocalDateTime time) {
        List<VideoVo> videoVoList = videoMapper.listVideoAfterTime(time);
        return videoVoList;
    }

    @Override
    public String statusVideo(AuditVideoDto auditVideoDto) {
        Video video = BeanUtil.copyProperties(auditVideoDto, Video.class);
        boolean success = videoMapper.updateById(video) > 0;
        if (success) {
            return "审核成功";
        } else {
            throw new BusinessException("审核失败");
        }
    }

    @Override
    public String praiseVideo(UpdateWorkInfoDto updateWorkInfoDto) {
        //1.参数校验
        Long videoId = Optional.ofNullable(updateWorkInfoDto.getWorksId())
                .orElseThrow(() -> new BusinessException("视频Id不能为空"));
        Long userId = Optional.ofNullable(updateWorkInfoDto.getUserId())
                .orElseThrow(() -> new BusinessException("用户Id不能为空"));
        State praise = Optional.ofNullable(updateWorkInfoDto.getPraise())
                .orElseThrow(() -> new BusinessException("点赞状态不能为空"));
        if (State.CONFIRM.equals(praise)) {
            redisUtil.addSet(V_PRISE_SET_KEY_PREFIX + videoId, String.valueOf(userId));
        } else {
            redisUtil.removeSet(V_PRISE_SET_KEY_PREFIX + videoId, String.valueOf(userId));
        }
        sendPraiseMessage(updateWorkInfoDto);
        return State.CONFIRM.equals(praise) ? "点赞成功" : "取消点赞";
    }

    @Override
    public String collectVideo(UpdateWorkInfoDto updateWorkInfoDto) {
        //1.参数校验
        Long videoId = Optional.of(updateWorkInfoDto.getWorksId())
                .orElseThrow(() -> new BusinessException("文章Id不能为空"));
        Long userId = Optional.of(updateWorkInfoDto.getUserId())
                .orElseThrow(() -> new BusinessException("用户Id不能为空"));
        State collect = Optional.ofNullable(updateWorkInfoDto.getCollect())
                .orElseThrow(() -> new BusinessException("收藏状态不能为空"));
        //2.判断是否收藏过
        if (State.CONFIRM.equals(collect)) {
            redisUtil.addSet(V_COLLECT_SET_KEY_PREFIX + videoId, String.valueOf(userId));
        } else {
            redisUtil.removeSet(V_COLLECT_SET_KEY_PREFIX + videoId, String.valueOf(userId));
        }
        sendCollectMessage(updateWorkInfoDto);
        return State.CONFIRM.equals(collect) ? "收藏成功" : "取消收藏";
    }

    @Override
    public Integer updateView(UpdateWorkInfoDto updateWorkInfoDto) {
        //1.参数校验
        Optional.ofNullable(updateWorkInfoDto.getWorksId())
                .orElseThrow(() -> new BusinessException("文章Id不能为空"));
        Optional.ofNullable(updateWorkInfoDto.getUserId())
                .orElseThrow(() -> new BusinessException("用户Id不能为空"));
        Integer incrementView = Optional.ofNullable(updateWorkInfoDto.getViewCount()).orElse(1);
        updateWorkInfoDto.setViewCount(incrementView);
        Integer view = redisUtil.increment(V_VIEW_COUNT_KEY_PREFIX + updateWorkInfoDto.getWorksId(), incrementView);
        sendViewMessage(updateWorkInfoDto);
        return view;
    }

    @Override
    public Integer updateShare(UpdateWorkInfoDto updateWorkInfoDto) {
        //1.参数校验
        Optional.ofNullable(updateWorkInfoDto.getWorksId())
                .orElseThrow(() -> new BusinessException("文章Id不能为空"));
        Optional.ofNullable(updateWorkInfoDto.getUserId())
                .orElseThrow(() -> new BusinessException("用户Id不能为空"));
        Integer incrementShare = Optional.ofNullable(updateWorkInfoDto.getShareCount())
                .orElse(1);
        updateWorkInfoDto.setShareCount(incrementShare);
        Integer share = redisUtil.increment(SHARE_COUNT_KEY_PREFIX + updateWorkInfoDto.getWorksId(), incrementShare);
        sendShareMessage(updateWorkInfoDto);
        return share;
    }

    @Override
    @GlobalTransactional
    public String deleteVideoById(Long id) {
        Video video = videoMapper.selectById(id);
        if (video == null) {
            return "视频不存在~";
        }
        String url = video.getVideoUrl();
        DeleteFileDto deleteFileDto = new DeleteFileDto();
        deleteFileDto.setUrl(url);
        String msg = fileClient.deleteFileByUrl(deleteFileDto).getMsg();
        return msg;
    }

    @Override
    @GlobalTransactional
    public String uploadVideo(Video video, MultipartFile file) {
        UserDto userDto = new UserDto();
        userDto.setId(video.getCreateOperator());
        UserVo userVo = userClient.getUserInfo(userDto).getData();
        video.setAuthor(userVo.getNickname());
        String fileName = file.getOriginalFilename();
        assert fileName != null;
        String suffix = getSuffix(fileName);
        boolean allowed = checkVideo(suffix);
        if (!allowed) {
            throw new BusinessException("请选择正确的视频格式");
        }
        String url = fileClient.uploadFile(fileName, file).getData();
        video.setVideoUrl(url);
        videoMapper.insert(video);
        return url;
    }

    @Override
    public String updateVideo(UpdateVideoDto updateVideoDto) {
        Integer count = videoMapper.update(updateVideoDto);
        if (count > 0) {
            return "更新成功";
        } else {
            throw new BusinessException("更新失败");
        }
    }


    private String getSuffix(String fileName) {
        try {
            return fileName.substring(fileName.lastIndexOf(".") + 1);
        } catch (StringIndexOutOfBoundsException e) {
            return "";
        }
    }

    private boolean checkVideo(String suffix) {
        // 视频格式校验
        return allowedSuffixes.contains(suffix);
    }

    private void sendPraiseMessage(UpdateWorkInfoDto updateWorkInfoDto) {
        amqpTemplate.convertAndSend("ls.media.exchange", "praise", updateWorkInfoDto);
    }

    private void sendCollectMessage(UpdateWorkInfoDto updateWorkInfoDto) {
        amqpTemplate.convertAndSend("ls.media.exchange", "collect", updateWorkInfoDto);
    }

    private void sendViewMessage(UpdateWorkInfoDto updateWorkInfoDto) {
        amqpTemplate.convertAndSend("ls.media.exchange", "view", updateWorkInfoDto);
    }

    private void sendShareMessage(UpdateWorkInfoDto updateWorkInfoDto) {
        amqpTemplate.convertAndSend("ls.media.exchange", "share", updateWorkInfoDto);
    }
}
